using Godot;
using System;
using System.Reflection;
using System.Collections.Generic;
using Box.Base;

namespace Box.GameCore {
    public enum RegisterType {
        Undefined,
        Entity,
        Block,
        Item,
        Biome,
        Event,
    }

    public class RegisterItem {
        public Type Type; 
        public bool IsCreateSingleton;
        public object Singleton;
        public T GetSingleton<T>() where T: class,new(){
            return Singleton as T;
        }
        public T CreateInstance<T>() where T : class,new(){
            return Activator.CreateInstance(Type) as T;
        }
    }

    public class RegisterBaseTypeAttribute : Attribute {
        public string Name;
        public bool IsCreateSingleton;
        public RegisterBaseTypeAttribute(string name,bool is_create_singleton = false) {
            Name = name;
            IsCreateSingleton = is_create_singleton;
        }
    }

    public class Register : Singleton<Register> {
        public class NotScene :Exception {
            public NotScene(string path):base($"不存在场景. -> {path}"){}
        }

        protected Dictionary<string,(Type type,bool is_create_singleton)> RegisterSetting =new Dictionary<string, (Type type, bool is_create_singleton)>();

        protected Dictionary<string,Dictionary<string,RegisterItem>> Registers = new Dictionary<string, Dictionary<string, RegisterItem>>();
    
        public Dictionary<string,RegisterItem> GetRegister(Type t) {
            foreach(var item in RegisterSetting) {
                if(item.Value.type.IsAssignableFrom(t)) {
                    return Registers[item.Key];
                }
            }

            return null;
        }

        public string GetRegisterType(Type t) {
            foreach(var item in RegisterSetting) {
                if(item.Value.type.IsAssignableFrom(t)) {
                    return item.Key;
                }
            }

            return "";
        }

        public Register() {
            LoadRegisters(); 
            LoadRegisterItems();
        }

        protected void LoadRegisters() {
            Type RegisterBaseType = typeof(RegisterBaseTypeAttribute);
            var assembly = Assembly.GetExecutingAssembly();
            foreach(Type type in assembly.GetTypes()) { 
                RegisterBaseTypeAttribute config = (RegisterBaseTypeAttribute)Attribute.GetCustomAttribute(type,RegisterBaseType);
                if(config != null) {
                    AddRegister(type,config.Name,config.IsCreateSingleton);
                }
            }
        }

        protected void LoadRegisterItems() {
            Type RegisterAttributeType = typeof(RegisterAttribute);
            var assembly = Assembly.GetExecutingAssembly();
            foreach(Type type in assembly.GetTypes()) { 
                RegisterAttribute register_info = (RegisterAttribute)Attribute.GetCustomAttribute(type,RegisterAttributeType);
                if(register_info != null) {
                    var register = GetRegister(type);
                    if(register != null) {
                        var register_type = GetRegisterType(type);
                        var register_setting = RegisterSetting[register_type];
                        RegisterItem item = new RegisterItem();
                        item.Type = type;

                        if(register_info.IsCreateSingleton || register_setting.is_create_singleton) {
                            item.Singleton = Activator.CreateInstance(type);
                        }
                        register[register_info.RegisterName] = item;
                    }
                }
            }
        }

        protected bool HasRegister(Type base_type,string name) {
            if(Registers.ContainsKey(name)) return true;
            foreach(var item in RegisterSetting.Values) {
                if(item.type.FullName == base_type.FullName) {
                    return true;
                }
            }
            return false;
        }

        public bool AddRegister(Type base_type,string name,bool is_singleton = false) {
            if(HasRegister(base_type,name)) return false;

            RegisterSetting[name] = (base_type,is_singleton);
            Registers[name] = new Dictionary<string, RegisterItem>();

            return true;
        }

        protected Node TryCreatePackedScene(Type type) {
            SceneInstanceAttribute scene_instance_info = (SceneInstanceAttribute)Attribute.GetCustomAttribute(type,typeof(SceneInstanceAttribute));
            if(scene_instance_info == null) return null;
            PackedScene packed_scene = GD.Load<PackedScene>(scene_instance_info.ScenePath);
            if(packed_scene == null) {
                throw new NotScene(scene_instance_info.ScenePath);
            }
            return packed_scene.Instantiate();
        }


        public RegisterItem GetRegisterItem(string register_name,string type_name) {
            var register = Registers[register_name];
            if(!register.ContainsKey(type_name)) return null;
            return  register[type_name];
        }

        public T GetRegisterItemSingleton<T>(string register_name,string type_name) where T:class,new() {
            var item = GetRegisterItem(register_name,type_name);
            if(item == null) return default(T);
            return item.GetSingleton<T>();
        }

        public T Create<T>(string register_name,string type_name) where T : class {
            var item = GetRegisterItem(register_name,type_name);
            if(item == null) return null;
            return Activator.CreateInstance(item.Type) as T;
        }

    }
}